package org.tinygroup.template.loader;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.tinygroup.fileresolver.FileResolver;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.template.Template;
import org.tinygroup.template.TemplateEngine;
import org.tinygroup.template.impl.TemplateEngineDefault;
import org.tinygroup.vfs.FileObject;
import org.tinygroup.vfs.FileObjectFilter;
import org.tinygroup.vfs.FileObjectProcessor;
import org.tinygroup.vfs.VFS;

/**
 * 模板文件资源管理器(页面文件、布局文件和宏文件)
 * 
 * @author yancheng11334
 * 
 */
public class FileResourceManager {

	protected static final Logger LOGGER = LoggerFactory
			.getLogger(FileResourceManager.class);

	/**
	 * 文件资源
	 */
	private Map<String, FileObject> resources = new ConcurrentHashMap<String, FileObject>();

	/**
	 * 地址映射
	 */
	private Map<String, List<String>> pathCaches = new ConcurrentHashMap<String, List<String>>();

	/**
	 * 文件扫描器
	 */
	private FileResolver fileResolver;

	private List<FileObject> scanFileList = new ArrayList<FileObject>();

	public FileResolver getFileResolver() {
		return fileResolver;
	}

	public void setFileResolver(FileResolver fileResolver) {
		this.fileResolver = fileResolver;
	}

	public void addScanFile(FileObject file) {
		if (!scanFileList.contains(file)) {
			scanFileList.add(file);
		}
	}

	/**
	 * 批量增加某个VFS路径下的模板引擎相关资源
	 * 
	 * @param engine
	 * @param root
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void addResources(final TemplateEngine engine,
			final FileObject root, final String templateExtName,
			final String layoutExtName, final String componentExtName) {
		addScanFile(root);
		root.foreach(new FileObjectFilter() {
			public boolean accept(FileObject fileObject) {
				String name = fileObject.getFileName();
				return (templateExtName != null && name
						.endsWith(templateExtName))
						|| (layoutExtName != null && name
								.endsWith(layoutExtName))
						|| (componentExtName != null && name
								.endsWith(componentExtName));
			}
		}, new FileObjectProcessor() {
			public void process(FileObject fileObject) {
				LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]开始加载",
						fileObject.getAbsolutePath());
				try {
					addResource(fileObject.getPath(), fileObject);
					Template template = TemplateLoadUtil.loadComponent(
							(TemplateEngineDefault) engine, fileObject);
					engine.getRepositories()
							.put(fileObject.getPath(), template);
					if (componentExtName != null
							&& fileObject.getFileName().endsWith(
									componentExtName)) {
						engine.registerMacroLibrary(template);
					}
				} catch (Exception e) {
					LOGGER.errorMessage("加载模板资源文件[{0}]出错,注册主键[{1}]", e,
							fileObject.getAbsolutePath(), fileObject.getPath());
				}
				LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]加载完毕,注册主键[{1}]",
						fileObject.getAbsolutePath(), fileObject.getPath());
			}
		});
	}

	/**
	 * 批量增加某个VFS路径下的模板引擎相关资源
	 * 
	 * @param engine
	 * @param resource
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void addResources(final TemplateEngine engine,
			final String resource, final String templateExtName,
			final String layoutExtName, final String componentExtName) {
		FileObject root = VFS.resolveFile(resource);
		addResources(engine, root, templateExtName, layoutExtName,
				componentExtName);
	}

	/**
	 * 添加文件资源
	 * 
	 * @param path
	 * @param file
	 */
	public void addResource(String path, FileObject file) {
		// 处理文件资源
		resources.put(path, file);
		// 处理映射表
		String[] dirs = splitPath(path);
		if (dirs != null && dirs.length > 1) {
			String newPath = null;
			for (int i = 1; i < dirs.length; i++) {
				newPath = mergePath(dirs, i);
				List<String> paths = pathCaches.get(newPath);
				if (paths == null) {
					paths = new ArrayList<String>();
					pathCaches.put(newPath, paths);
				}
				paths.add(path); // 将资源表的键值地址存入列表
			}
		}
	}

	public void removeScanFile(FileObject file) {
		scanFileList.remove(file);
	}

	/**
	 * 批量删除某个VFS路径下的模板引擎相关资源
	 * 
	 * @param engine
	 * @param root
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void removeResources(final TemplateEngine engine,
			final FileObject root, final String templateExtName,
			final String layoutExtName, final String componentExtName) {
		removeScanFile(root);
		root.foreach(new FileObjectFilter() {
			public boolean accept(FileObject fileObject) {
				String name = fileObject.getFileName();
				return (templateExtName != null && name
						.endsWith(templateExtName))
						|| (layoutExtName != null && name
								.endsWith(layoutExtName))
						|| (componentExtName != null && name
								.endsWith(componentExtName));
			}
		}, new FileObjectProcessor() {
			public void process(FileObject fileObject) {
				LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]开始移除",
						fileObject.getAbsolutePath());
				try {
					removeResource(fileObject.getPath());
					engine.removeTemplate(fileObject.getPath());
				} catch (Exception e) {
					LOGGER.errorMessage("移除模板资源文件[{0}]出错,卸载主键[{1}]", e,
							fileObject.getAbsolutePath(), fileObject.getPath());
				}
				LOGGER.logMessage(LogLevel.INFO, "模板资源文件[{0}]移除完毕,卸载主键[{1}]",
						fileObject.getAbsolutePath(), fileObject.getPath());
			}
		});
	}

	/**
	 * 批量删除某个VFS路径下的模板引擎相关资源
	 * 
	 * @param engine
	 * @param resource
	 * @param templateExtName
	 * @param layoutExtName
	 * @param componentExtName
	 */
	public void removeResources(final TemplateEngine engine,
			final String resource, final String templateExtName,
			final String layoutExtName, final String componentExtName) {
		FileObject root = VFS.resolveFile(resource);
		removeResources(engine, root, templateExtName, layoutExtName,
				componentExtName);
	}

	/**
	 * 删除文件资源
	 * 
	 * @param path
	 * @param file
	 */
	public void removeResource(String path) {
		// 处理文件资源
		resources.remove(path);
		// 处理映射表
		String[] dirs = splitPath(path);
		if (dirs != null && dirs.length > 1) {
			String newPath = null;
			for (int i = 1; i < dirs.length; i++) {
				newPath = mergePath(dirs, i);
				List<String> paths = pathCaches.get(newPath);
				if (paths != null) {
					paths.remove(path); // 删除资源表的键值地址
					if (paths.isEmpty()) {
						pathCaches.remove(newPath);
					}
				}
			}
		}
	}

	/**
	 * 查询已经注册文件资源
	 * 
	 * @param path
	 * @param tag
	 *            是否精确匹配
	 * @return
	 */
	public FileObject getFileObject(String path, boolean tag) {
		// 先直接地址匹配
		if (resources.containsKey(path)) {
			return resources.get(path);
		}

		if(!tag){
			// 再逐个目录删减，从地址映射表查询
			String[] dirs = splitPath(path);
			if (dirs != null && dirs.length > 0) {
				if (dirs.length == 1) {
					List<String> paths = pathCaches.get(path);
					if (paths != null && paths.size() > 0) { // 至少匹配一个
						return resources.get(paths.get(0));
					}
				} else {
					String newPath = null;
					// 查询与创建和删除不一样，遍历从第0个元素开始
					for (int i = 0; i < dirs.length; i++) {
						newPath = mergePath(dirs, i);
						List<String> paths = pathCaches.get(newPath);
						if (paths != null && paths.size() > 0) { // 至少匹配一个
							return resources.get(paths.get(0));
						}
					}
				}

			}
		}
		
		return null;
	}

	/**
	 * 查询已经注册文件资源
	 * 
	 * @param path
	 * @return
	 */
	public FileObject getFileObject(String path) {
		return getFileObject(path, false);
	}

	/**
	 * 查询注册模板资源以外的其他文件
	 * 
	 * @param path
	 * @return
	 */
	public FileObject getOtherFileObject(String path) {
		// 通过文件扫描器查询
		if (fileResolver != null) {
			for (String rootPath : fileResolver.getScanningPaths()) {
				FileObject root = VFS.resolveFile(rootPath);
				FileObject file = root.getFileObject(path);
				if (file != null) {
					return file;
				}
			}
		}
		// 非文件扫描器方式查询
		for (FileObject root : scanFileList) {
			FileObject file = root.getFileObject(path);
			if (file != null) {
				return file;
			}
		}

		return null;
	}

	/**
	 * 合并路径
	 * 
	 * @param dirs
	 * @param n
	 * @return
	 */
	private String mergePath(String[] dirs, int n) {
		StringBuilder sb = new StringBuilder();
		for (int i = n; i < dirs.length; i++) {
			sb.append("/").append(dirs[i]);
		}
		return sb.toString();
	}

	/**
	 * 拆分路径
	 * 
	 * @param path
	 * @return
	 */
	private String[] splitPath(String path) {
		path = path.startsWith("/") ? path.substring(1) : path;
		return path.split("/");
	}

	/**
	 * 文件是否被修改
	 * 
	 * @param path
	 * @return
	 */
	public boolean isModified(String path) {
		return isModified(path,false);
	}
	
	/**
	 * 文件是否被修改
	 * 
	 * @param path
	 * @return
	 */
	public boolean isModified(String path,boolean tag) {
		FileObject fileObject = getFileObject(path,tag);
		if (fileObject == null) {
			return true;
		}
		return fileObject.isModified();
	}


	/**
	 * 重置文件修改标识
	 * 
	 * @param path
	 */
	public void resetModified(String path) {
		resetModified(path,false);
	}
	
	/**
	 * 重置文件修改标识
	 * 
	 * @param path
	 */
	public void resetModified(String path,boolean tag) {
		FileObject fileObject = getFileObject(path,tag);
		if (fileObject != null) {
			fileObject.resetModified();
		}
	}
}
